/************************************************************************/
/*                                                                      */
/*  Typecast Project                                                    */
/*                                                                      */
/*  Kernel module for Packet Filtering via NetFilter                    */
/*  Author: Eusden Shing, UCLA                                          */
/*                                                                      */
/************************************************************************/


/************************************************************************/
/*                                                                      */
/*  Typecast Project                                                    */
/*                                                                      */
/*  Kernel module for Packet Filtering via NetFilter                    */
/*  Author: Eusden Shing, UCLA                                          */
/*          2-11-2007                                                   */
/*                                                                      */
/************************************************************************/

#include <linux/config.h>
#include <linux/module.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>

static struct nf_hook_ops typecast_ops[2];

static unsigned int typecast_hook(unsigned int hook,
struct sk_buff **pskb,
    const struct net_device *indev,
    const struct net_device *outdev,
    int (*okfn)(struct sk_buff *))
{
    if (MULTICAST( ((*pskb)->nh.iph)->daddr ) )
    {
        printk("Caught a packet!\n");
        return NF_QUEUE;
    }

    return NF_ACCEPT;
}

static int __init typecast_init(void)
{
    printk("typecast_kmod init - entered.\n");
    int i, nfret;

    // Initialize hook specifications
    typecast_ops[0].list.next = NULL;
    typecast_ops[0].list.prev = NULL;
    typecast_ops[0].hook = typecast_hook;
    typecast_ops[0].owner = NULL;
    typecast_ops[0].pf = PF_INET;
    typecast_ops[0].hooknum = NF_IP_LOCAL_IN;
    typecast_ops[0].priority = NF_IP_PRI_FILTER + 100; // make it bigger so that MAODV's hook get invoked first. 

    typecast_ops[1].list.next = NULL;
    typecast_ops[1].list.prev = NULL;
    typecast_ops[1].hook = typecast_hook;
    typecast_ops[1].owner = NULL;
    typecast_ops[1].pf = PF_INET;
    typecast_ops[1].hooknum = NF_IP_POST_ROUTING;
    typecast_ops[1].priority = NF_IP_PRI_FILTER + 100;

    // Register the hooks
    for (i = 0; i < sizeof(typecast_ops) / sizeof(struct nf_hook_ops); i++)
    {
        nfret = nf_register_hook(&typecast_ops[i]);
        if (nfret < 0)
            break;
    }

    if (nfret < 0)
    {
        for (i = 0; i < sizeof(typecast_ops) / sizeof(struct nf_hook_ops); i++)
            nf_unregister_hook(&typecast_ops[i]);
    }

    printk("typecast_kmod init - exited.\n");
    return nfret;
}

static void __exit typecast_exit(void)
{
    printk("typecast_kmod exit - entered.\n");

    int i;

    for (i = 0; i < sizeof(typecast_ops) / sizeof(struct nf_hook_ops); i++)
        nf_unregister_hook(&typecast_ops[i]);

    printk("typecast_kmod exit - exited.\n");
}

module_init(typecast_init);
module_exit(typecast_exit);


// 
// #include <linux/slab.h>
// #include <linux/config.h>
// #include <linux/module.h>
// #include <linux/netfilter_ipv4.h>
// #include <linux/ip.h>
// #include <linux/udp.h>
// #include <linux/socket.h>
// #include <linux/kernel.h>
// #include <linux/netlink.h>
// #include <linux/in.h>
// #include <net/sock.h>
// 
// #include "typecast-rt.h"
// 
// struct sock *nl_sk_set = NULL;
// struct sock *nl_sk_dump = NULL;
// RoutingTableEntry* RoutingTable;
// int RoutingTableCount = 0;
// int iInitialized = 0;
// 
// static unsigned int typecast_hook(unsigned int hooknum,
//                                   struct sk_buff **pskb,
//                                   const struct net_device *indev,
//                                   const struct net_device *outdev,
//                                   int (*okfn)(struct sk_buff *))
// {
//     struct sk_buff *skb = *pskb;
//     struct nlmsghdr *nlh = NULL;
//     char tempPacketBloomFilter[BLOOMFILTERSIZE + 1];
//     char packetBloomFilter[BLOOMFILTERSIZE/2 + 1];
//     int i = 0;
//     int j = 0;
//     int iFound = 0;
// 
//     // Is RoutingTable initialized?
//     if (iInitialized != 0)
//     {
// 
//         // Only handle MULTICAST packets.
//         if (MULTICAST( ((*pskb)->nh.iph)->daddr ) )
//         {
//             // Clear out packetBloomFilter memory.
//             memset(packetBloomFilter, 0, BLOOMFILTERSIZE/2 + 1);
//             memset(tempPacketBloomFilter, 0, BLOOMFILTERSIZE + 1);
// 
//             // Get the UDP payload
//             struct iphdr *iph = (struct iphdr*)skb->data;
//             struct udphdr *uh = (struct udphdr*)(skb->data+(iph->ihl<<2));
//             printk("udp src=%d dst=%d\n", uh->source, uh->dest); 
//             char * udp_payload = (char *)uh + sizeof(struct udphdr); 
//             memcpy(tempPacketBloomFilter, udp_payload , BLOOMFILTERSIZE);
// 
//             // Convert BloomFilter from char-array format to byte-format.
//             //   (tempPacketBloomFilter -> packetBloomFilter)
//             // tempPacketBloomFilter is the raw Bloom filter taken from the
//             // packet payload, in char-array format.  This needs to be converted
//             // to byte-format for comparison.
//             for (j = 0; j < BLOOMFILTERSIZE; j++)
//             {
//                 int tempInt;
//                 if (('0' <= tempPacketBloomFilter[j]) && (tempPacketBloomFilter[j] <= '9'))
//                     tempInt = tempPacketBloomFilter[j] - '0';
//                 else if (('A' <= tempPacketBloomFilter[j]) && (tempPacketBloomFilter[j] <= 'F'))
//                     tempInt = tempPacketBloomFilter[j] - 'A' + 0xA;
//                 else if (('a' <= tempPacketBloomFilter[j]) && (tempPacketBloomFilter[j] <= 'f'))
//                     tempInt = tempPacketBloomFilter[j] - 'a' + 0xA;
// 
//                 printk("j=%d, tempInt=%x\n", j, tempInt);
// 
//                 if ((j % 2) == 0)
//                     packetBloomFilter[j/2] = (tempInt << 4);
//                 else
//                     packetBloomFilter[j/2] += (tempInt);
//             }
// 
// /*
//             //////////////////////////////////////////////////////////////////////////
//             // THIS IS FOR DEBUGGING PURPOSES:
// 
//             // Convert BloomFilter from char-array format to byte-format.
//             //   (tempPacketBloomFilter -> packetBloomFilter)
//             for (j = 0; j < BLOOMFILTERSIZE; j++)
//             {
//                 int tempInt = 0;
//                 if ((j % 2) == 0)
//                     tempInt = (packetBloomFilter[j/2] >> 4);
//                 else
//                     tempInt = (packetBloomFilter[j/2] & 15);
// 
//                 if (tempInt < 10)
//                     tempPacketBloomFilter[j] = tempInt + '0';
//                 else
//                     tempPacketBloomFilter[j] = tempInt + 'A' - 10;
//             }
// 
//             printk("PacketBloomFilter(char) = %s, PacketBloomFilter(byte) = %s\n", tempPacketBloomFilter, packetBloomFilter);
// 
//             // END DEBUGGING PURPOSE
//             //////////////////////////////////////////////////////////////////////////
// */
//             if (hooknum == NF_IP_LOCAL_IN)
//             {
//                 // Validate the first RoutingTable entry.
//                 // Both the source address and destination address must be 255.255.255.255, which is 4294967295.
//                 if ((RoutingTable[0].srcAddr != 4294967295) ||
//                     (RoutingTable[0].dstAddr != 4294967295))
//                 {
//                     printk("Error in routing table.  First routing entry: saddr is %s, daddr is %s.\n",
//                             (*pskb)->nh.iph->saddr, (*pskb)->nh.iph->daddr);
//                     return NF_ACCEPT;
//                 }
// 
//                 // Apply BloomFilter to see if we want to drop this packet.              
//                 for (i = 0; i < BLOOMFILTERSIZE/2; i++)
//                 {
//                 // Q: Is routing table local BloomFilter a subfilter of the packet BloomFilter?
//                     if ((packetBloomFilter[i] | RoutingTable[0].BloomFilter[i]) != RoutingTable[0].BloomFilter[i])
//                     {
//                 // No, so drop packet.
//                         printk("Local BloomFilter is not a subfilter of packet BloomFilter.  Dropping.\n");
//                         return NF_DROP;
//                     }
//                 }
// 
//                 // Yes, so accept the packet.
//                 return NF_ACCEPT;
//             }
//             else if (hooknum == NF_IP_FORWARD)
//             {
//                 // Check from the second RoutingTable entry (j = 1) onward
//                 // (1st entry is only for NF_IP_LOCAL)
//                 for (j = 1, iFound = 0; (j < RoutingTableCount); j++)
//                 {
//                     // Q: Does this BloomFilter apply?
//                     if ((RoutingTable[j].srcAddr == (*pskb)->nh.iph->saddr) &&
//                         (RoutingTable[j].dstAddr == (*pskb)->nh.iph->daddr))
//                     {
//                         // Yes.
//                         printk("Found a matching Routing Table BloomFilter.\n");
// 
//                         // Q: Is routing table BloomFilter a subfilter of the packet BloomFilter?
//                         for (i = 0; i < BLOOMFILTERSIZE/2; i++)
//                         {
//                             if ((RoutingTable[j].BloomFilter[i] | skb->head[i]) != RoutingTable[j].BloomFilter[i])
//                             {
//                                 // No.
//                                 break;
//                             }
//                         }
// 
//                         if (i == BLOOMFILTERSIZE/2)
//                         {
//                             // Yes.  Break out of loop and accept packet.
//                             iFound = 1;
//                             break;
//                         }
//                     }
//                 }
// 
//                 if (iFound == 1)
//                 {
//                     printk("Routing Table BloomFilter is a subfilter of packet BloomFilter.  Accepting.\n");
//                     return NF_ACCEPT;
//                 }
//                 else
//                 {
//                     printk("Routing Table BloomFilter is not a subfilter of packet BloomFilter.  Dropping.\n");
//                     return NF_DROP;
//                 }
//             }
//             else
//             {
//                 printk("hooknum = %d?\n", hooknum);
//             }
//         }
//     }
//     else //(iInitialized != 0)
//     {
//         // Routing table has not been initialized yet.
//         return NF_ACCEPT;
//     }
// }
// 
// static struct nf_hook_ops typecast_ops[] =
// { 
//     {
//         { NULL, NULL },
//         typecast_hook,
//         NULL, 
//         PF_INET,
//         NF_IP_LOCAL_IN,
//         NF_IP_PRI_FILTER-1
//     },
//     {
//         { NULL, NULL },
//         typecast_hook,
//         NULL, 
//         PF_INET,
//         NF_IP_FORWARD,
//         NF_IP_PRI_FILTER
//     }
// };
// 
// void nl_data_ready_set (struct sock *sk, int len)
// {
//     struct sk_buff *skb = NULL;
//     struct nlmsghdr *nlh = NULL;
//     RoutingTableEntry rteTemp;
//     int err;
//     int i;
//     u32 pid;
// 
//     /* wait for message coming down from user-space */
//     skb = skb_recv_datagram(nl_sk_set, 0, 0, &err);
// 
//     // NLMSG_DATA gives a CHAR* offset at the first byte of payload
//     nlh = (struct nlmsghdr *)skb->data;
// 
// 
//     // Find the packet can have variable number of RoutingTableEntry(s).  We will stuff
//     // RoutingTable with the entries one by one, until we find a NULL entry.
//     // The packet MUST terminate with a NULL entry, and must be serialized in an array format.
// 
//     // The first routing table entry MUST have srcAddr = dstAddr = inet_addr(255.255.255.255),
//     // and an appropriate BloomFilter, indicating that this is the NF_LOCAL_IN BloomFilter.
// 
//     RoutingTableCount = 0;
//     do
//     {
//         memcpy( &rteTemp, NLMSG_DATA(nlh) + (sizeof(RoutingTableEntry) * RoutingTableCount), sizeof(RoutingTableEntry) );
//         RoutingTableCount++;
//     } while (rteTemp.srcAddr != 0);
// 
//     RoutingTable = kmalloc( sizeof(RoutingTableEntry) * RoutingTableCount, GFP_KERNEL);
// 
//     for (i = 0; i < RoutingTableCount; i++)
//     {
//         memcpy( &(RoutingTable[i]), NLMSG_DATA(nlh) + (sizeof(RoutingTableEntry) * i), sizeof(RoutingTableEntry) );
//     }
// 
//     iInitialized = 1;
// }
// 
// void nl_data_ready_dump (struct sock *sk, int len)
// {
//     struct sk_buff *skb = NULL;
//     struct nlmsghdr *nlh = NULL;
//     RoutingTableEntry rteTemp;
//     int err;
//     int i;
//     u32 pid;
// 
//     /* wait for message coming down from user-space */
//     skb = skb_recv_datagram(nl_sk_dump, 0, 0, &err);
// 
//     if (iInitialized == 1)
//     {
//         nlh = (struct nlmsghdr *)skb->data;
// 
//         // Just dump the entire RoutingTable into the buffer.  We will translate in userspace
//         memcpy( NLMSG_DATA(nlh), RoutingTable, sizeof(RoutingTableEntry) * RoutingTableCount );
//         // Add a terminating entry
//         memset( NLMSG_DATA(nlh) + sizeof(RoutingTableEntry) * RoutingTableCount, 0, sizeof(RoutingTableEntry));
// 
//         pid = nlh->nlmsg_pid;
//         //NETLINK_CB(skb).groups = 0;
//         NETLINK_CB(skb).pid = 0;
//         NETLINK_CB(skb).dst_pid = pid;
//         NETLINK_CB(skb).dst_groups = 0;
//         netlink_unicast(nl_sk_dump, skb, pid, MSG_DONTWAIT);
//     }
//     else
//         printk("RoutingTable has not yet been initialized.\n");
// }
// 
// static int __init typecast_init(void)
// {
//     int i, nfret;
// 
//     printk("typecast_init entered.\n");
// 
//     // Register netfilter hooks.
//     for (i = 0; i < sizeof(typecast_ops) / sizeof(struct nf_hook_ops); i++)
//     {
//         nfret = nf_register_hook(&typecast_ops[i]);
//         if (nfret < 0)
//             break;
//     }
// 
//     if (nfret < 0)
//     {
//         // Failed to register hooks.  Unregister and fail.
// 
//         for (i = 0; i < sizeof(typecast_ops) / sizeof(struct nf_hook_ops); i++)
//             nf_unregister_hook(&typecast_ops[i]);
// 
//         printk("typecast_init FAILED.\n");
//         return nfret;
//     }
// 
//     nl_sk_set = netlink_kernel_create(NETLINK_TYPECAST_SET_RT, 0, nl_data_ready_set, THIS_MODULE);
//     nl_sk_dump = netlink_kernel_create(NETLINK_TYPECAST_DUMP_RT, 0, nl_data_ready_dump, THIS_MODULE);
// 
//     //nl_sk_set = netlink_kernel_create(NETLINK_TYPECAST_SET_RT, nl_data_ready_set);
//     //nl_sk_dump = netlink_kernel_create(NETLINK_TYPECAST_DUMP_RT, nl_data_ready_dump);
// 
//     printk("typecast_init exited.\n");
// 
//     return nfret;
// }
// 
// static void __exit typecast_exit(void)
// {
//     int i;
// 
//     printk("typecast_exit entered.\n");
// 
//     // Unregister hooks.
//     for (i = 0; i < sizeof(typecast_ops) / sizeof(struct nf_hook_ops); i++)
//         nf_unregister_hook(&typecast_ops[i]);
// 
//     // Release the sockets.
//     if (nl_sk_set->sk_socket)
//         sock_release(nl_sk_set->sk_socket);
//     if (nl_sk_dump->sk_socket)
//         sock_release(nl_sk_dump->sk_socket);
// 
//     printk("typecast_exit exited.\n");
// }
// 
// module_init(typecast_init);
// module_exit(typecast_exit);
